//
//  CHSesame5Device+History.swift
//  SesameSDK
//  [history]read/get/post
//  Created by tse on 2023/3/9.
//  Copyright © 2023 CandyHouse. All rights reserved.
//

extension CHSesame5Device {
    
    func readHistoryCommand(_ result: @escaping (CHResult<CHEmpty>))  {
        L.d("[ss5][history] readHistoryCommand <=")
        URLSession.isInternetReachable { isInternetReachable in
//            L.d("[ss5][history] 連網?",isInternetReachable)
            let deleteHistory = isInternetReachable ? "01":"00"
            self.sendCommand(.init( .history, deleteHistory.hexStringtoData())) { (result) in
                if result.cmdResultCode == .success {
//                    L.d("[ss5][history]result.data => ",result.data.toHexLog())
                    let histitem = result.data.copyData
                    let recordId = histitem[0...3].copyData
//                    var recordId = histitem[0...3].copyData ///log用
//                    L.d("[ss5][history]id => ", recordId.toInt32() )
                    let type = histitem[4...4].copyData
                    let timeData = histitem[5...8].copyData
                    let mechStatusData = histitem[9...15].copyData
                    var hisContent = histitem[16...].copyData // historyTag
                    var hisContentPayload = hisContent
                    L.d("[ss5][history]歷史標籤 =>", hisContent.toHexLog())
//                  var historyType: Sesame2HistoryTypeEnum = Sesame2HistoryTypeEnum(rawValue: type.bytes[0])!
                    guard var historyType = Sesame2HistoryTypeEnum(rawValue: type.bytes[0]) else { return }
                    let record_id_Int32: Int32 = recordId.withUnsafeBytes({ $0.bindMemory(to: Int32.self).first! })
                    let timestampInt32: UInt32 = timeData.withUnsafeBytes({ $0.bindMemory(to: UInt32.self).first! })
//                    L.d("[ss5][history]",historyType.plainName)
                    //[joi todo] bug:藍芽開關鎖會掉歷史
                    if historyType == .BLE_LOCK || historyType == .BLE_UNLOCK {
                        guard hisContent.count > 0 else {
                            hisContent = Data()
                            return
                        }
                        var tagcount = hisContent[0] % Sesame2HistoryLockOpType.BASE.rawValue
                        var historyOpType = hisContent[0] / Sesame2HistoryLockOpType.BASE.rawValue
                        let hisTag =  self.sesame2KeyData?.historyTag ?? Data()
                        if tagcount == (hisContent[1] % Sesame2HistoryLockOpType.BASE.rawValue) + 1, hisTag.count != tagcount {
                            historyOpType = hisContent[1] / Sesame2HistoryLockOpType.BASE.rawValue
                            tagcount = hisContent[1] % Sesame2HistoryLockOpType.BASE.rawValue
                            hisContent = histitem[17...].copyData
                        }
                        if Sesame2HistoryLockOpType(rawValue: historyOpType) == .WM2 {
                            historyType = historyType == .BLE_LOCK ? Sesame2HistoryTypeEnum.WM2_LOCK : Sesame2HistoryTypeEnum.WM2_UNLOCK
                        } else if Sesame2HistoryLockOpType(rawValue: historyOpType) == .WEB {
                            historyType = historyType == .BLE_LOCK ? Sesame2HistoryTypeEnum.WEB_LOCK : Sesame2HistoryTypeEnum.WEB_UNLOCK
                        }
                        hisContentPayload = hisContent.copyData
                        guard hisContent.count > tagcount else { return }
                        if tagcount < 1 {
                            hisContent = tagcount.data + Data()
                        } else {
                            hisContent = tagcount.data + hisContent[1...tagcount].copyData
                        }
                    }
                    let chHistoryEvent = CHSesame5HistoryEvent(type:historyType,time:UInt64(timestampInt32)*1000,recordID:record_id_Int32,content:hisContent,parameter:mechStatusData) // Event用在post
                    if let sesame2History = self.eventToCHHistroy(historyEvent: chHistoryEvent) {
                        (self.delegate as? CHSesame5Delegate)?.onHistoryReceived(device: self, result: .success(CHResultStateBLE(input: [sesame2History])))
                    }
                    guard isInternetReachable && !self.isConnectedByWM2 else { return }
                    //保持s5 history content UI 和 aws落库数据一致性，降低后期维护成本
                    let historyData = recordId + type + timeData + mechStatusData + hisContentPayload
                    self.postProcessHistory(historyData) { res in
                        if case .success(_) = res  {
                            self.sendCommand(.init(SesameItemCode.historyDelete, recordId)) { response in
                                if response.cmdResultCode == .success  { L.d("[ss5][history]歷史删除成功") }
                            }
                        }
                    }
//                    if self.isHistory {
//                        self.readHistoryCommand() { _ in }
//                    }
                }else{
                    (self.delegate as? CHSesame5Delegate)?.onHistoryReceived(device: self, result: .failure(self.errorFromResultCode(result.cmdResultCode)))
                    self.isHistory = false
                }
            }
        }
    }
}

extension CHSesame5Device {
    
    func getHistories(cursor: UInt?, _ result: @escaping CHResult<CHSesame5HistoryPayload>) {
//        L.d("[ss5][history] getHistories")
        guard let getKey = getKey() else {
            // Key has been deleted
            return
        }
        let secretKey = getKey.sesame2PublicKey
        var queryParams: [String: Any] = ["lg": 15, "a": secretKey]
        if let cursor = cursor {
            queryParams["cursor"] = cursor
        }
        CHAccountManager.shared
            .API(request: .init(.get, "/device/v2/sesame2/\(self.deviceId.uuidString)/history", queryParameters: queryParams)) { requestResult in

                switch requestResult {
                case .success(let data):
                    //                    L.d("[ss5][history]",data)
                    guard let historyResponse = try? JSONDecoder().decode(CHSesameHistory5Response.self, from: data ?? Data()) else {
                        result(.failure(NSError.parseError))
                        return
                    }

//                    historyResponse.histories.forEach { (his) in
//                        L.d(his.type)
//                        self.eventToCHHistroy( historyEvent: his)!
//                    }

                    let sesame2Histories = historyResponse.histories.compactMap { self.eventToCHHistroy( historyEvent: $0) }

                    result(.success(CHResultStateNetworks(input: .init(histories: sesame2Histories, cursor: historyResponse.cursor))))
                case .failure(let error):
                    result(.failure(error))
                }
            }
    }

    func postProcessHistory(_ historyData: Data, _ callback: @escaping CHResult<CHEmpty>) {
//        L.d("[ss5][history]post")
        let request: CHAPICallObject = .init(.post, "/device/v1/sesame2/historys", [
            "s": self.deviceId.uuidString,
            "v": historyData.toHexString(),
            "t":"5",
        ])

        CHAccountManager
            .shared
            .API(request: request) { result in
                switch result {
                case .success(_):
                    L.d("[ss5][history]藍芽", "上傳歷史成功")
                    callback(.success(CHResultStateNetworks(input:CHEmpty())))
                    break
                case .failure(let error):
                    L.d("[ss5][history]藍芽", "上傳歷史失敗,server掉歷史: \(error)")
                    callback(.failure(error))
                    break
                }
            }
    }
}

public class CHSesame5HistoryData {
    required init(historyEvent: CHSesame5HistoryEvent) {
        self.recordID = historyEvent.recordID
        self.historyTag = historyEvent.historyTag
        self.date = Date(timeIntervalSince1970: TimeInterval(historyEvent.timeStamp/1000))
        self.timestamp = historyEvent.timeStamp
        self.mechStatus = historyEvent.parameter == nil ? nil : Sesame5MechStatus.fromData(historyEvent.parameter!)
    }
    
    public let recordID:Int32
    public let historyTag: Data?
    public let date: Date
    public let timestamp: UInt64
    public let mechStatus: CHSesameProtocolMechStatus?
}

public enum CHSesame5History {
    case manualLocked(CHSesame5HistoryData)
    case manualUnlocked(CHSesame5HistoryData)
    case bleLock(CHSesame5HistoryData)
    case bleUnlock(CHSesame5HistoryData)
    case autoLock(CHSesame5HistoryData)
    case wm2Lock(CHSesame5HistoryData)
    case wm2Unlock(CHSesame5HistoryData)
    case webLock(CHSesame5HistoryData)
    case webUnlock(CHSesame5HistoryData)

}

extension CHSesame5Device {
    func eventToCHHistroy(historyEvent: CHSesame5HistoryEvent) -> CHSesame5History? {
        switch Sesame2HistoryTypeEnum(rawValue: historyEvent.type) {
        case .AUTOLOCK:
            return CHSesame5History.autoLock(CHSesame5HistoryData(historyEvent: historyEvent))
        case .BLE_LOCK:
            return CHSesame5History.bleLock(CHSesame5HistoryData(historyEvent: historyEvent))
        case .BLE_UNLOCK:
            return CHSesame5History.bleUnlock(CHSesame5HistoryData(historyEvent: historyEvent))
        case .WM2_UNLOCK:
            return CHSesame5History.wm2Unlock(CHSesame5HistoryData(historyEvent: historyEvent))
        case .WM2_LOCK:
            return CHSesame5History.wm2Lock(CHSesame5HistoryData(historyEvent: historyEvent))
        case .MANUAL_LOCKED:
            return CHSesame5History.manualLocked(CHSesame5HistoryData(historyEvent: historyEvent))
        case .MANUAL_UNLOCKED:
            return CHSesame5History.manualUnlocked(CHSesame5HistoryData(historyEvent: historyEvent))
        case .WEB_LOCK:
            return CHSesame5History.webLock(CHSesame5HistoryData(historyEvent: historyEvent))
        case .WEB_UNLOCK:
            return CHSesame5History.webUnlock(CHSesame5HistoryData(historyEvent: historyEvent))
        @unknown default:
            return nil
        }
    }
}
